/*
 * ListSerializer_test.cpp
 *
 *  Created on: Oct 24, 2012
 *      Author: m
 */

#include <memory>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdlib>

#include "VariableStringSerializer.h"
#include "ListSerializer.h"

#define _MIN_ARGS 3
#define _MODE_ARG 1
#define _INFILE_ARG 2
#define _OUTFILE_ARG 3
#define _SERIALIZE_MODE "-s"
#define _DESERIALIZE_MODE "-d"
#define _BIN_BUFFER_SIZE 256*100

typedef enum { serialize, deserialize, invalid } operation_mode_t;

void invalid_args()
{
    std::cerr << "Argumentos inválidos. " << std::endl
              <<"Uso: ls_ts [" << _SERIALIZE_MODE << "|" << _DESERIALIZE_MODE << "] input output"
              << std::endl;
    exit( 1 );
}

void invalid_mode()
{
    std::cerr << "Modo inválido. " << std::endl
              <<"Uso: ls_ts [" << _SERIALIZE_MODE << "|" << _DESERIALIZE_MODE << "] input output"
              << std::endl;
    exit( 2 );
}

operation_mode_t parse_mode( char* arg )
{
    if ( strcmp( arg, _SERIALIZE_MODE ) == 0 )
        return serialize;

    if ( strcmp( arg, _DESERIALIZE_MODE ) == 0 )
        return deserialize;

    return invalid;
}

int main( int argc, char* argv[] )
{
    std::fstream textFile;
    std::fstream binFile;
    std::shared_ptr < std::list < std::shared_ptr < std::string > > > plist;
    std::shared_ptr < std::string > strBuffer;
    std::string lineBuffer;
    size_t serializedBytes;
    std::shared_ptr < physical::VariableSizeRecordSerializer<std::string> > stringSerializer;
    stringSerializer.reset( new physical::VariableStringSerializer );
    physical::ListSerializer < std::string > listSerializer( stringSerializer );
    physical::byte binaryBuffer[_BIN_BUFFER_SIZE];
    size_t deserializedBytes;
    size_t deserializedBytesAcumulator;
    std::unique_ptr< physical::byte > inputBuffer;
    physical::byte* deserializationPtr;
    std::streampos fsize;
    std::streampos currentPosition;

    int deserializedLinesCount;

    if ( argc < _MIN_ARGS+1 )
        invalid_args();

    operation_mode_t mode = parse_mode( argv[_MODE_ARG] );

    switch ( mode )
    {
        case serialize:
            textFile.open( argv[_INFILE_ARG], std::ios_base::in );
            binFile.open( argv[_OUTFILE_ARG], std::ios_base::out | std::ios_base::trunc | std::ios_base::binary );

            /*
             *
             * Leo los renglones uno a uno, cada renglón se considera una lista de palabras.
             * Luego de leer cada renglón creo construyo una lista de palabras y finalmente la serializo
             *
             * */

            while( getline( textFile, lineBuffer ) )
            {
                // creo una nueva lista
                plist.reset( new std::list < std::shared_ptr < std::string > > );

                // La cargo con las palabras de la frase leida.
                std::stringstream ss( lineBuffer );

                strBuffer.reset( new std::string );
                while( ss >> *strBuffer )
                {
                    plist->push_back( strBuffer );
                    strBuffer.reset( new std::string );
                }

                serializedBytes = listSerializer.serialize( plist, binaryBuffer );
                binFile.write( binaryBuffer, serializedBytes );
            }

            textFile.close();
            binFile.close();
            break;

        case deserialize:

            // Se lee tod0 el archivo a memoria (esto es prque no estoy trabajando con bloques para las pruebas
            binFile.open( argv[_INFILE_ARG], std::ios_base::in | std::ios_base::binary );
            textFile.open( argv[_OUTFILE_ARG], std::ios_base::out | std::ios_base::trunc );

            // calculo el tamaño del archivo de entrada y lo cargo tod0 en memoria
            fsize = 0;
            currentPosition = binFile.tellg();

            // Calculo tamaño del archivo
            binFile.clear();
            binFile.seekg( 0, std::ios::beg );
            fsize = binFile.tellg();
            binFile.clear();
            binFile.seekg( 0, std::ios::end );
            fsize = binFile.tellg() - fsize;
            binFile.clear();
            binFile.seekg( currentPosition );

            inputBuffer.reset( new physical::byte[fsize] );
            binFile.read( inputBuffer.get() , fsize );

            deserializationPtr = inputBuffer.get();

            deserializedBytesAcumulator = 0;

            deserializedLinesCount = 0;

            // Mientras haya cosas para deserializar ene le buffer...
            while ( deserializedBytesAcumulator < fsize )
            {
                deserializedLinesCount++;

                /*
                // Separo la nueva lista de la lista anterior por un endl (en el archivo de salida)
                if ( deserializedBytesAcumulator > 0 )
                {
                    textFile << '\n';
                }
                */

                //deserializo la lista
                deserializedBytes = listSerializer.deserialize( deserializationPtr, plist );

                // Imprimo uno a uno sus elementos, separados por un espacio.
                std::list< std::shared_ptr< std::string > >::iterator it;
                for ( it=plist->begin(); it!=plist->end(); it++ )
                {
                    if ( it==plist->begin() )
                        textFile << *(*it);
                    else
                        textFile << " " << *(*it);
                }

                textFile << std::endl;

                // preparo variables auxiliares para la proxima deserialización
                deserializationPtr += deserializedBytes;
                deserializedBytesAcumulator += deserializedBytes;
            }

            textFile.close();
            binFile.close();
            break;

        case invalid:
            invalid_mode();
            break;
    }

    return 0;
}
